﻿"""Classes which fetch needed info from web pages"""

import urllib
import re
import datetime
import BeautifulSoup
from google.appengine.api import urlfetch
from errors import UnexistingStopError

class QueryProcessor:
    """Abstract class which fetches needed info from web pages"""
    def __init__(self):
        raise NotImplementedError('You need to inherit me')
   
    def get_stop_info(self, stop_number):
        """Get name, description, services of stop stop_number"""
        raise NotImplementedError('You need to inherit me')
    
    def get_vehicles(self, stop_number, service):
        """Get vehicle scheduled times for service service at stop stop_number"""
        raise NotImplementedError('You need to inherit me')
        
class QueryProcessorSKGT(QueryProcessor):
    """Class which fetches needed info from SKGT web pages"""
    SELECT_BY_STOP_URL = 'http://gps.skgt-bg.com/Web/SelectByStop.aspx'
    DRAW_IMAGE_URL = 'http://gps.skgt-bg.com/Services/DrawImage.ashx'
    
    def fetch(self, url, cookie=None, post_vars={}, raw=False, debug=False):
        response = urlfetch.fetch(url=url,
                                payload=urllib.urlencode(post_vars),
                                method=(urlfetch.POST if post_vars else urlfetch.GET),
                                headers={'Cookie':cookie})
        if debug:
            print response.content
        if response.status_code != 200:
            raise BadStatusCodeError(response)
        result = response.content if raw else BeautifulSoup.BeautifulSoup(response.content)
        return (result, response.headers.get('Set-Cookie', cookie).split(';')[0])

    def get_form_vars(self, soup):
        form_tag = soup.find('form')
        input_tags = form_tag.findAll('input')
        return dict([(tag.get('name',''), tag.get('value','')) for tag in input_tags])

    def parse_vehicle_name(self, str):
        map = {u'автобус':'A', u'трамвай':'T', u'тролей':'TB'}
        (type, number) = str.split()
        return map.get(type, 'Z') + number

    def fetch_select_by_stop_home(self):
        (soup, cookie) = self.fetch(self.SELECT_BY_STOP_URL, '_c=y')
        form_vars = self.get_form_vars(soup)
        return {'cookie':cookie, 'form_vars':form_vars}
        
    def fetch_stop_info(self, number, cookie, form_vars):
        # get page containing list of lines for this stop
        form_vars = form_vars.copy()
        form_vars['ctl00$ContentPlaceHolder1$tbStopCode'] = str(number)
        form_vars['__EVENTTARGET'] = ''
        form_vars['__EVENTARGUMENT'] = ''
        form_vars['__LASTFOCUS'] = ''
        form_vars['ctl00$ContentPlaceHolder1$btnSearchLine.x'] = '50'
        form_vars['ctl00$ContentPlaceHolder1$btnSearchLine.y'] = '18'
        del form_vars['ctl00$ContentPlaceHolder1$btnRefresh']
        (soup, cookie) = self.fetch(self.SELECT_BY_STOP_URL, cookie, form_vars)
        form_vars = self.get_form_vars(soup)
        
        # parse the page containing list of lines for this stop
        select_tag = soup.find('form').find('select')
        option_tags = select_tag.findAll('option')
        service_codes = dict([(self.parse_vehicle_name(tag.string), tag.get('value','')) for tag in option_tags[1:]])
        try:
            name = soup.find('span', attrs={'id' : 'ctl00_ContentPlaceHolder1_lblStopName'}).string.lower()
            description = soup.find('span', attrs={'id' : 'ctl00_ContentPlaceHolder1_lblDescription'}).string.lower().replace('\n', '')
            services = sorted(service_codes.keys(), key=lambda s: s[0] + s[1:].zfill(4))
        except AttributeError:
            raise UnexistingStopError(number)
        
        return {'stop_info':{'name':name, 'description':description, 'services':services}, 'internal':{'service_codes':service_codes, 'cookie':cookie, 'form_vars':form_vars}}

    def fetch_service_vehicles(self, service_code, cookie, form_vars):
        form_vars = form_vars.copy()
        form_vars['ctl00$ContentPlaceHolder1$ddlLine'] = service_code
        (soup, cookie) = self.fetch(self.SELECT_BY_STOP_URL, cookie, form_vars)
        
        #(picture, cookie) = self.fetch(self.DRAW_IMAGE_URL, cookie, form_vars, raw=True, debug=True)
        
        #span_tags = soup.findAll('span', attrs={'id' : re.compile('^ctl00_ContentPlaceHolder1_gvTimes_')})
        span_tags = soup.findAll('div', attrs={'id' : re.compile('^ctl00_ContentPlaceHolder1_gvTimes_')})

        vehicles_list = []
        for tag in span_tags:
            # example string to be matched u'02:20 изчислено в: 00:33 08.07.2009'
            m = re.match(ur'(\d\d):(\d\d) изчислено в: (\d\d):(\d\d) (\d\d).(\d\d).(\d\d\d\d)', tag.string)
            generation_dt = datetime.datetime(*[int(s) for s in m.group(7, 6, 5, 3, 4)])
            scheduled_dt = datetime.datetime.combine(generation_dt, datetime.time(*[int(s) for s in m.group(1, 2)]))
            if scheduled_dt < generation_dt:
                # correct scheduled time if vehicle comes after midnight
                scheduled_dt += datetime.timedelta(days=1)
            vehicles_list.append(scheduled_dt)
        return vehicles_list
    
    def __init__(self):
        d = self.fetch_select_by_stop_home()
        self.home_cookie = d['cookie']
        self.home_form_vars = d['form_vars']
        self.stops = {}
   
    def get_stop_info(self, stop_number):
        """Get name, description, services of stop stop_number"""
        d = self.fetch_stop_info(stop_number, self.home_cookie, self.home_form_vars)
        self.stops[stop_number] = d['internal']
        return d['stop_info']
    
    def get_vehicles(self, stop_number, service):
        """Get vehicle scheduled times for service service at stop stop_number"""
        if stop_number not in self.stops:
            self.get_stop_info(stop_number)
        internal = self.stops[stop_number]
        return self.fetch_service_vehicles(internal['service_codes'][service], internal['cookie'], internal['form_vars'])

class QueryProcessorTest(QueryProcessor):
    """Class which generates given constant output"""
    
    def __init__(self, now, stops, vehicles=None):
        self.now = now
        self.stops = stops
        if vehicles is not None:
            self.vehicles = vehicles
        else:
            self.vehicles = {}
            for stop_number in stops.keys():
                for service in stops[stop_number].services:
                    n = random.randint(0, 6)
                    self.vehicles[(stop_number, service)] = map(lambda x: random.randint(0,90), [None]*n)
            
    def get_stop_info(self, stop_number):
        """Get name, description, services of stop stop_number"""
        if stop_number in self.stops:
            return self.stops[stop_number]
        else:
            raise UnexistingStopError(stop_number)
    
    def get_vehicles(self, stop_number, service):
        """Get vehicle scheduled times for service service at stop stop_number"""
        return map(lambda t: self.now + datetime.timedelta(minutes=t), self.vehicles[(stop_number, service)])
